namespace GrpcSampleDbAccessService
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Text;
    using System.Threading.Tasks;
    using DbAccess.proto;
    using Grpc.Core;
    using GrpcSampleDbAccessService.Context;
    using GrpcSampleDbAccessService.DbContext;
    using GrpcSampleDbAccessService.Model;
    using GrpcSampleDbAccessService.Repository;
    using Microsoft.Extensions.Logging;
    using Stee.CAP8.DBAccess;

    public class DbAccessService : DbAccessGrpc.DbAccessGrpcBase
    {
        private readonly ILogger<DbAccessService> _logger;
        private readonly StudentContext _stdContext;
        private readonly IStudentRepository _stdRepository;
        private readonly SubjectContext _subjContext;
        private readonly IRepository<Subject> _subjRepository;
        public DbAccessService(ILogger<DbAccessService> logger, StudentContext stdContext, IStudentRepository stdRepository, SubjectContext subjContext, IRepository<Subject> subjRepository)
        {
            _logger = logger;
            _stdContext = stdContext;
            _stdRepository = stdRepository;
            _subjContext = subjContext;
            _subjRepository = subjRepository;
        }
        public override async Task<ResultMessageResponse> AddSample(NoParameterRequest request, ServerCallContext context)
        {
            var std = new Student { Name = "Billy", Gender = "GrpcConsoleClient" };
            _stdRepository.Add(std);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "A record has been added (Name: " + std.Name + ", Gender:" + std.Gender + ")" };
        }
        public override async Task<ResultMessageResponse> AddSubjectSample(NoParameterRequest request, ServerCallContext context)
        {
            var sbj = new Subject { Name = "Mathematics", TaughtBy = "GrpcConsoleClient" };
            _subjRepository.Add(sbj);
            await _subjContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "A record has been added (Name: " + sbj.Name + ", TaughtBy:" + sbj.TaughtBy + ")" };
        }
        public override async Task<ResultMessageResponse> CustomAddSample(NoParameterRequest request, ServerCallContext context)
        {
            var std = new Student { Name = "Billy", Gender = "GrpcConsoleClient" };
            _stdRepository.CustomAdd(std);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "A record has been added (Name: " + std.Name + ", Gender:" + std.Gender + ")" };
        }
        public override async Task<ResultMessageResponse> AddRangeIEnumSample(NoParameterRequest request, ServerCallContext context)
        {
            var stdList = new List<Student>();
            for (var count = 0; count < 20; count++)
            {
                if (count < 10)
                {
                    stdList.Add(new Student { Name = "0" + count.ToString(CultureInfo.InvariantCulture), Gender = "AddRangeIEnumSample" });
                }
                else
                {
                    stdList.Add(new Student { Name = count.ToString(CultureInfo.InvariantCulture), Gender = "AddRangeIEnumSample" });
                }
            }
            _stdRepository.AddRange(stdList);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "A list of 20 records has been added via AddRangeIEnumSample." };
        }
        public override async Task<ResultMessageResponse> AddRangeParamTSample(NoParameterRequest request, ServerCallContext context)
        {
            var std1 =
                new Student { Name = "ParamT1", Gender = "AddRangeParamTSample" };
            var std2 =
                new Student { Name = "ParamT2", Gender = "AddRangeParamTSample" };
            _stdRepository.AddRange(std1, std2);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "2 records has been added via AddRangeParamTSample." };
        }
        public override async Task<ResultMessageResponse> GetListSample(NoParameterRequest request, ServerCallContext context)
        {
            var stdList = _stdRepository.GetList();
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            var message = new StringBuilder();
            foreach (var s in stdList)
            {
                message.Append("ID(" + s.ID.ToString(CultureInfo.InvariantCulture) + "), Name(" + s.Name + "), Gender(" + s.Gender + ")  \r\n");
            }
            return new ResultMessageResponse { ResultMessage = message.ToString() };
        }
        public override async Task<ResultMessageResponse> GetListAndDeleteSample(NoParameterRequest request, ServerCallContext context)
        {
            var stdList = await _stdRepository.GetListAsync(activeDatastate: false).ConfigureAwait(false);
            foreach (var s in stdList)
            {
                _stdRepository.Delete(s, softDelete: false);
            }
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "Delete All: SUCCESS" };
        }
        public override async Task<ResultMessageResponse> PaginationSample(PaginationRequest request, ServerCallContext context)
        {
            var orderBy = new List<OrderBy>
            {
                new OrderBy { PropertyName = "Name" }
            };
            if (request == null)
            {
                return null;
            }
            var stdList = _stdRepository.GetList(null, orderBy, request.PageIndex, request.PageSize);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);

            var message = new StringBuilder();
            foreach (var s in stdList)
            {
                message.Append(s.Name + "\r\n");
            }
            return new ResultMessageResponse { ResultMessage = message.ToString() };
        }
        public override async Task<ResultMessageResponse> SingleSample(SampleIDRequest request, ServerCallContext context)
        {
            if (request == null)
            {
                return null;
            }
            var result = await _stdRepository.GetSingleAsync(long.Parse(request.ID, CultureInfo.InvariantCulture)).ConfigureAwait(false);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "The Student named " + result.Name + " is " + result.Gender + "." };
        }
        public override async Task<ResultMessageResponse> SingleOrDefaultSample(SampleIDRequest request, ServerCallContext context)
        {
            if (request == null)
            {
                return null;
            }
            var result = await _stdRepository.GetSingleOrDefaultAsync(long.Parse(request.ID, CultureInfo.InvariantCulture)).ConfigureAwait(false);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);

            var message = "There is no record found.";
            if (result != null)
            {
                message = "The Sample named " + result.Name + " is " + result.Gender + ".";
            }
            return new ResultMessageResponse { ResultMessage = message };
        }
        public override async Task<ResultMessageResponse> UpdateSample(SampleIDRequest request, ServerCallContext context)
        {
            if (request == null)
            {
                return null;
            }
            var result = _stdRepository.GetSingleOrDefault(long.Parse(request.ID, CultureInfo.InvariantCulture));
            if (result == null)
            {
                return null;
            }
            result.Name += " (Updated)";
            _stdRepository.Update(result);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            var message = "The Sample named " + result.Name + " is updated.";
            return new ResultMessageResponse { ResultMessage = message };
        }
        public override async Task<ResultMessageResponse> UpdateByPropertyNameSample(UpdatePropertyNameValueRequest request, ServerCallContext context)
        {
            if (request == null)
            {
                return null;
            }
            _stdRepository.UpdateProperty(long.Parse(request.ID, CultureInfo.InvariantCulture), request.PropertyName, request.PropertyValue);
                await _stdContext.SaveChangesAsync().ConfigureAwait(false);
                return new ResultMessageResponse { ResultMessage = "Updated property by " + request.ID };
        }
        public override async Task<ResultMessageResponse> DeleteSample(SampleIDRequest request, ServerCallContext context)
        {
            if (request == null)
            {
                return null;
            }
            var result = _stdRepository.GetSingleOrDefault(long.Parse(request.ID, CultureInfo.InvariantCulture));
            _stdRepository.Delete(result);
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "Deleted record (" + request.ID + ")" };
        }
        public override async Task<ResultMessageResponse> DeleteByIDSample(SampleIDRequest request, ServerCallContext context)
        {
            if (request == null)
            {
                return null;
            }
            _stdRepository.DeleteByID(long.Parse(request.ID, CultureInfo.InvariantCulture));
            await _stdContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "Deleted record (" + request.ID + ")" };
        }

        public override async Task<ResultMessageResponse> TransactionSample(NoParameterRequest request, ServerCallContext context)
        {
            _stdContext.BeginTransaction();
            try
            {
                AddRangeIEnumNoTransSample();
                AddRangeIEnumNoTransSample();
                await _stdContext.CommitTransactionAsync().ConfigureAwait(false);
                return new ResultMessageResponse { ResultMessage = "Transaction is commited via TransactionSample." };
            }
            catch (MultipleTransactionException ex)
            {
                return new ResultMessageResponse { ResultMessage = ex.ToString() };
            }
        }
        public override async Task<ResultMessageResponse> RollbackSample(NoParameterRequest request, ServerCallContext context)
        {
            var message = "";
            _stdContext.BeginTransaction();
            try
            {
                await GetListAndDeleteSample(request, context).ConfigureAwait(false);
                ThrowException();
                await _stdContext.CommitTransactionAsync().ConfigureAwait(false);
                message = "Transaction via RollbackSample is commited";
            }
            catch (Exception)
            {
                _stdContext.RollbackTransaction();
                message = "Transaction via RollbackSample failed";
            }
            return new ResultMessageResponse { ResultMessage = message };
        }
        public override async Task<ResultMessageResponse> GetListSubjectSample(NoParameterRequest request, ServerCallContext context)
        {
            var subjList = _subjRepository.GetListAsync(activeDatastate:false);
            await _subjContext.SaveChangesAsync().ConfigureAwait(false);
            var message = new StringBuilder();
            foreach (var s in subjList.Result)
            {
                message.Append("ID(" + s.ID.ToString(CultureInfo.InvariantCulture) + "), Name(" + s.Name + "), TaughtBy(" + s.TaughtBy + ")  \r\n");
            }
            return new ResultMessageResponse { ResultMessage = message.ToString() };
        }
        public override async Task<ResultMessageResponse> DeleteSubjectSample(SampleIDRequest request, ServerCallContext context)
        {
            if (request == null)
            {
                return null;
            }
            var result = _subjRepository.GetSingleOrDefault(long.Parse(request.ID, CultureInfo.InvariantCulture));
            _subjRepository.Delete(result, softDelete: false);
            await _subjContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "Deleted record (" + request.ID + ")" };
        }
        public override async Task<ResultMessageResponse> DeleteByIDSubjectSample(SampleIDRequest request, ServerCallContext context)
        {
            if (request == null)
            {
                return null;
            }
            _subjRepository.DeleteByID(long.Parse(request.ID, CultureInfo.InvariantCulture), softDelete: false);
            await _subjContext.SaveChangesAsync().ConfigureAwait(false);
            return new ResultMessageResponse { ResultMessage = "Deleted record (" + request.ID + ")" };
        }

        private void ThrowException()
        {
            throw new Exception();
        }
        private void AddRangeIEnumNoTransSample()
        {
            var stdList = new List<Student>();
            for (var count = 1; count < 20; count++)
            {
                if (count < 10)
                {
                    stdList.Add(new Student { Name = "0" + count.ToString(CultureInfo.InvariantCulture), Gender = "AddRangeIEnumNoTransSample TEST" });
                }
                else
                {
                    stdList.Add(new Student { Name = count.ToString(CultureInfo.InvariantCulture), Gender = "AddRangeIEnumNoTransSample TEST" });

                }
            }
            _stdRepository.AddRange(stdList);
        }
    }
}
